/**
 * Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jfinal.template.expr.ast;

/**
 * FieldKeyBuilder
 * 
 * 用于生成缓存 FieldGetter 的 key
 */
public abstract class FieldKeyBuilder {

    public abstract Object getFieldKey(Class<?> targetClass, long fieldFnv1a64Hash);

    // 假定是超大规模项目，并且假定其 Map/Model/Record + field 组合数量超级庞大，默认使用 StrictFieldKeyBuilder
    // static FieldKeyBuilder instance = new StrictFieldKeyBuilder();
    static FieldKeyBuilder instance = new FastFieldKeyBuilder();

    public static FieldKeyBuilder getInstance() {
        return instance;
    }

    /**
     * 开启 FastFieldKeyBuilder，性能更高
     */
    public static void setFastFieldKeyBuilder(boolean enable) {
        if (enable) {
            instance = new FastFieldKeyBuilder();
        } else {
            instance = new StrictFieldKeyBuilder();
        }
    }

    /**
     * 设置为自定义 FieldKeyBuilder
     */
    public static void setFieldKeyBuilder(FieldKeyBuilder fieldKeyBuilder) {
        if (fieldKeyBuilder == null) {
            throw new IllegalArgumentException("fieldKeyBuilder can not be null");
        }
        instance = fieldKeyBuilder;
    }

    // ------------------------------------------------------------------------

    /**
     * FastFieldKeyBuilder
     */
    public static class FastFieldKeyBuilder extends FieldKeyBuilder {
        public Object getFieldKey(Class<?> targetClass, long fieldFnv1a64Hash) {
            return targetClass.getName().hashCode() ^ fieldFnv1a64Hash;
        }
    }

    // ------------------------------------------------------------------------

    /**
     * StrictFieldKeyBuilder
     */
    public static class StrictFieldKeyBuilder extends FieldKeyBuilder {
        public Object getFieldKey(Class<?> targetClass, long fieldFnv1a64Hash) {
            return new FieldKey(targetClass.getName().hashCode(), fieldFnv1a64Hash);
        }
    }

    // ------------------------------------------------------------------------

    /**
     * FieldKey
     *
     * FieldKey 用于封装 targetClass、fieldName 这两部分的 hash 值，
     * 确保不会出现 key 值碰撞
     *
     * 这两部分 hash 值在不同 class 与 field 的组合下出现碰撞的
     * 概率完全可以忽略不计
     *
     * 备忘：
     * 可以考虑用 ThreadLocal 重用 FieldKey 对象，但要注意放入 Map fieldGetterCache
     * 中的 FieldKey 对象需要 clone 出来，确保线程安全。由于 FieldKey 占用空间不大，
     * 所以 ThreadLocal 方案大概率并无优势，从 ThreadLocal 中获取数据时，除了耗时也无法
     * 避免创建对象
     */
    public static class FieldKey {

        final int classHash;
        final long fieldHash;

        public FieldKey(int classHash, long fieldHash) {
            this.classHash = classHash;
            this.fieldHash = fieldHash;
        }

        public int hashCode() {
            return classHash ^ (int)fieldHash;
        }

        /**
         * FieldKey 的核心价值在于此 equals 方法通过比较两部分 hash 值，避免超大规模场景下可能的 key 值碰撞
         *
         * 不必判断 if (fieldKey instanceof FieldKey)，因为所有 key 类型必须要相同
         * 不必判断 if (this == fieldKey)，因为每次用于取值的 FieldKey 都是新建的
         */
        public boolean equals(Object fieldKey) {
            FieldKey fk = (FieldKey)fieldKey;
            return classHash == fk.classHash && fieldHash == fk.fieldHash;
        }

        public String toString() {
            return "classHash = " + classHash + "\nfieldHash = " + fieldHash;
        }
    }
}








